package model.tokens;

import java.util.Deque;
import java.util.LinkedList;
import java.util.Stack;
import java.util.Queue;

import model.semantics.LeftGroupNode;
import model.semantics.Node;

/**
 * Represents a token created from symbols in an expression string.
 */
public abstract class Token<T> {
    protected String myLabel;

    public Token(TokenConstructorArguments<T> args) {
        myLabel = args.symbol;
    }

    /**
     * Process the Shunting-Yard algorithm for the token.
     * 
     * @param operatorStack
     *            the algorithm's operator stack.
     * @param outputQueue
     *            the output queue for the algorithm.
     */
    public abstract void process(Automaton<T> state);

    /**
     * Shunt functions that can be instantiated from the operator stack to the
     * output queue.
     * 
     * @param <T>
     *            the type of the nodes.
     * @param operatorStack
     *            the stack of operator tokens.
     * @param output
     *            the queue of output nodes.
     */
    public static <T> void shuntFunctionsToOutput(
            Stack<Token<T>> operatorStack, Deque<Node<T>> output) {
        // Pop operators off of the stack, make nodes out of them, and shunt
        // them to the output queue.
        while (!operatorStack.isEmpty()
                && operatorStack.peek() instanceof FunctionalToken<?>) {
            FunctionalToken<T> operator = (FunctionalToken<T>) operatorStack
                    .peek();

            // The output queue is large enough to make functions out of.
            if (operator.getOrder() <= output.size()) {
                operatorStack.pop();
                Node<T> instance = operator.makeNewInstance(output);
                output.add(instance);
            } else
                break;
        }
    }

    /**
     * Gets the label for the token.
     * 
     * @return the label of the token as a string.
     */
    protected String getLabel() {
        return myLabel;
    }

    @Override
    public String toString() {
        return myLabel;
    }
}
